{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4be2e6fa-2187-4617-8433-0db4fb0c099c",
   "metadata": {},
   "source": [
    "# LangChain 核心模块学习：Model I/O\n",
    "\n",
    "`Model I/O` 是 LangChain 为开发者提供的一套面向 LLM 的标准化模型接口，包括模型输入（Prompts）、模型输出（Output Parsers）和模型本身（Models）。\n",
    "\n",
    "- Prompts：模板化、动态选择和管理模型输入\n",
    "- Models：以通用接口调用语言模型\n",
    "- Output Parser：从模型输出中提取信息，并规范化内容\n",
    "\n",
    "![](../images/model_io.jpeg)\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2e64b01e-f5ad-4614-b0c3-a140f6bb575a",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (0.0.340)\n",
      "Requirement already satisfied: requests<3,>=2 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (2.31.0)\n",
      "Requirement already satisfied: PyYAML>=5.3 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (6.0)\n",
      "Requirement already satisfied: dataclasses-json<0.7,>=0.5.7 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.5.7)\n",
      "Requirement already satisfied: pydantic<3,>=1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.10.8)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (3.8.5)\n",
      "Requirement already satisfied: tenacity<9.0.0,>=8.1.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (8.2.2)\n",
      "Requirement already satisfied: anyio<4.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (3.6.2)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.4.41)\n",
      "Requirement already satisfied: async-timeout<5.0.0,>=4.0.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (4.0.2)\n",
      "Requirement already satisfied: langsmith<0.1.0,>=0.0.63 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.0.66)\n",
      "Requirement already satisfied: jsonpatch<2.0,>=1.33 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.33)\n",
      "Requirement already satisfied: numpy<2,>=1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.26.2)\n",
      "Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (3.1.0)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.3)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.4)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.2)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.1.0)\n",
      "Requirement already satisfied: idna>=2.8 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from anyio<4.0->langchain) (3.4)\n",
      "Requirement already satisfied: sniffio>=1.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from anyio<4.0->langchain) (1.3.0)\n",
      "Requirement already satisfied: typing-inspect>=0.4.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (0.9.0)\n",
      "Requirement already satisfied: marshmallow<4.0.0,>=3.3.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (3.19.0)\n",
      "Requirement already satisfied: marshmallow-enum<2.0.0,>=1.5.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (1.5.1)\n",
      "Requirement already satisfied: jsonpointer>=1.9 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from jsonpatch<2.0,>=1.33->langchain) (2.3)\n",
      "Requirement already satisfied: typing-extensions>=4.2.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from pydantic<3,>=1->langchain) (4.6.2)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (2023.5.7)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (1.26.16)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from SQLAlchemy<3,>=1.4->langchain) (2.0.2)\n",
      "Requirement already satisfied: packaging>=17.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from marshmallow<4.0.0,>=3.3.0->dataclasses-json<0.7,>=0.5.7->langchain) (23.1)\n",
      "Requirement already satisfied: mypy-extensions>=0.3.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from typing-inspect>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain) (1.0.0)\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "# 安装最新版本的 LangChain Python SDK（https://github.com/langchain-ai/langchain）\n",
    "!pip install -U langchain"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce4a2474-0b69-4830-85cd-3715c22df304",
   "metadata": {},
   "source": [
    "## 输出解析器 Output Parser\n",
    "\n",
    "**语言模型的输出是文本。**\n",
    "\n",
    "但很多时候，您可能希望获得比纯文本**更结构化的信息**。这就是输出解析器的价值所在。\n",
    "\n",
    "输出解析器是帮助结构化语言模型响应的类。它们必须实现两种主要方法：\n",
    "\n",
    "- \"获取格式指令\"：返回一个包含有关如何格式化语言模型输出的字符串的方法。\n",
    "- \"解析\"：接受一个字符串（假设为来自语言模型的响应），并将其解析成某种结构。\n",
    "\n",
    "然后还有一种可选方法：\n",
    "- \"使用提示进行解析\"：接受一个字符串（假设为来自语言模型的响应）和一个提示（假设为生成此响应的提示），并将其解析成某种结构。在需要重新尝试或修复输出，并且需要从提示中获取信息以执行此操作时，通常会提供提示。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f14f4cf-8e30-47ab-b8b1-d58a90b5b1c1",
   "metadata": {},
   "source": [
    "### 列表解析 List Parser\n",
    "\n",
    "当您想要返回一个逗号分隔的项目列表时，可以使用此输出解析器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0089c8a5-a859-49f2-bec0-fcd84f2f3b56",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers import CommaSeparatedListOutputParser\n",
    "from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate\n",
    "from langchain.llms import OpenAI\n",
    "\n",
    "# 创建一个输出解析器，用于处理带逗号分隔的列表输出\n",
    "output_parser = CommaSeparatedListOutputParser()\n",
    "\n",
    "# 获取格式化指令，该指令告诉模型如何格式化其输出\n",
    "format_instructions = output_parser.get_format_instructions()\n",
    "\n",
    "# 创建一个提示模板，它会基于给定的模板和变量来生成提示\n",
    "prompt = PromptTemplate(\n",
    "    template=\"List five {subject}.\\n{format_instructions}\",  # 模板内容\n",
    "    input_variables=[\"subject\"],  # 输入变量\n",
    "    partial_variables={\"format_instructions\": format_instructions}  # 预定义的变量，这里我们传入格式化指令\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8d681566-cde1-4ae5-8cd7-f53cf59c3e36",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用提示模板和给定的主题来格式化输入\n",
    "_input = prompt.format(subject=\"ice cream flavors\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ef5ea022-b27f-4cc9-b6c8-5d4e96e51d51",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "List five ice cream flavors.\n",
      "Your response should be a list of comma separated values, eg: `foo, bar, baz`\n"
     ]
    }
   ],
   "source": [
    "print(_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4db222de-501b-4114-aaaa-03e54c2da228",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = OpenAI(temperature=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bd743ccf-c47f-4bde-a5a6-63052f6a2553",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = llm(_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "12a2bc49-2656-47e8-b5a4-ee119ab77004",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "Vanilla, Chocolate, Strawberry, Mint Chocolate Chip, Cookies and Cream\n"
     ]
    }
   ],
   "source": [
    "print(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0bb71bf2-3220-4326-a69c-7b0fa1864877",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Vanilla',\n",
       " 'Chocolate',\n",
       " 'Strawberry',\n",
       " 'Mint Chocolate Chip',\n",
       " 'Cookies and Cream']"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用之前创建的输出解析器来解析模型的输出\n",
    "output_parser.parse(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81fbd00c-5e03-4b23-a276-6acc0f5d5f1a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "bd93d8d7-7d77-4453-97a6-f7349090a370",
   "metadata": {},
   "source": [
    "### 日期解析 Datatime Parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9b91deaf-6d3f-4d48-a084-58ec1ec4b0b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers import DatetimeOutputParser\n",
    "from langchain.chains import LLMChain\n",
    "\n",
    "output_parser = DatetimeOutputParser()\n",
    "template = \"\"\"Answer the users question:\n",
    "\n",
    "{question}\n",
    "\n",
    "{format_instructions}\"\"\"\n",
    "\n",
    "prompt = PromptTemplate.from_template(\n",
    "    template,\n",
    "    partial_variables={\"format_instructions\": output_parser.get_format_instructions()},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "96ad1f11-c2d0-4bb5-a8ec-1b5dd5132573",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['question'] partial_variables={'format_instructions': 'Write a datetime string that matches the \\n            following pattern: \"%Y-%m-%dT%H:%M:%S.%fZ\". Examples: 1847-04-07T02:43:02.250267Z, 446-01-18T23:15:53.307833Z, 229-03-15T02:26:50.545980Z'} template='Answer the users question:\\n\\n{question}\\n\\n{format_instructions}'\n"
     ]
    }
   ],
   "source": [
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "1eca781f-0146-45e5-9848-bf8347513a77",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Answer the users question:\n",
      "\n",
      "around when was bitcoin founded?\n",
      "\n",
      "Write a datetime string that matches the \n",
      "            following pattern: \"%Y-%m-%dT%H:%M:%S.%fZ\". Examples: 1847-04-07T02:43:02.250267Z, 446-01-18T23:15:53.307833Z, 229-03-15T02:26:50.545980Z\n"
     ]
    }
   ],
   "source": [
    "print(prompt.format(question=\"around when was bitcoin founded?\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4f46d70f-78a4-4576-9583-4f67c1ab1d08",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = LLMChain(prompt=prompt, llm=OpenAI())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "97e3270b-43be-4018-b5c8-b2f7ffdbec46",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = chain.run(\"around when was bitcoin founded?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "dc7915dc-ef1e-4feb-8db1-83d51d773daa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\n\\n2008-01-03T18:15:05.000000Z'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "e9bfcab7-a335-4b36-8292-6ac92352222b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "datetime.datetime(2008, 1, 3, 18, 15, 5)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_parser.parse(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "2db4804c-7a1f-41df-99ed-cb7956387155",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2008-01-03 18:15:05\n"
     ]
    }
   ],
   "source": [
    "print(output_parser.parse(output))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a18b9b7e-cce2-4561-9030-21fdc5822d3f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
